#include "gui/SubWindow/Workspace.h"
#include "model/Utils.h"
#include "gui/Tabs.h"


Workspace::Workspace(Diagram* diagram, bool enableEvents) : DrawingAreaPlusPlus(enableEvents),
	m_diagram(diagram)
{
	_necesitaReacomodar = false;
	if ( enableEvents)
		add_events(Gdk::ENTER_NOTIFY_MASK);

	m_refPageSetup = Gtk::PageSetup::create();
	m_refSettings = Gtk::PrintSettings::create();
	m_diagramOffsetX = 0;
	m_diagramOffsetY = 0;
}

Workspace::~Workspace() {

}

void Workspace::imprimirAPng(std::string archivo) {
	double sizeX, sizeY;
	getSize(sizeX, sizeY);

	Cairo::RefPtr<Cairo::Surface> ptrSurface = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24, sizeX+50, sizeY+50);
	Cairo::RefPtr<Cairo::Context> ptrContext = Cairo::Context::create(ptrSurface);

	//Blanquear.
	ptrContext->set_source_rgb(1, 1, 1);
	ptrContext->paint();

	ptrContext->save();

	prepareScreenContext(ptrContext);
	ptrContext->restore();

	ptrContext->set_source_rgb(1, 1, 1);
	ptrContext->fill();
	ptrContext->get_target()->write_to_png(archivo);
}

void Workspace::imprimirAPdf(std::string archivo) {
	Glib::RefPtr<Gtk::PrintOperation> print = Gtk::PrintOperation::create();
	print_or_preview(print);
	print->set_export_filename(archivo);

	try {
		print->run(Gtk::PRINT_OPERATION_ACTION_EXPORT,
				dynamic_cast<Gtk::Window&> (*get_toplevel()));
	} catch (const Gtk::PrintError& ex) {
		std::cerr << "An error occurred while trying to run a print operation:"
				<< ex.what() << std::endl;
	}
}

void Workspace::print_or_preview(Glib::RefPtr<Gtk::PrintOperation> print) {
	print->signal_draw_page().connect(sigc::mem_fun(this,
			&Workspace::on_draw_page));

	print->set_n_pages(2);

	print->set_track_print_status();
	print->set_default_page_setup(m_refPageSetup);
	print->set_print_settings(m_refSettings);
}

void Workspace::on_menu_file_page_setup() {
	Glib::RefPtr<Gtk::PageSetup> new_page_setup = Gtk::run_page_setup_dialog(
			dynamic_cast<Gtk::Window&> (*get_toplevel()), m_refPageSetup,
			m_refSettings);
	m_refPageSetup = new_page_setup;
}

void Workspace::on_menu_file_print_preview() {
	Glib::RefPtr<Gtk::PrintOperation> print = Gtk::PrintOperation::create();
	print_or_preview(print);
	try {
		print->run(Gtk::PRINT_OPERATION_ACTION_PREVIEW,
				dynamic_cast<Gtk::Window&> (*get_toplevel()));
	} catch (const Gtk::PrintError& ex) {
		std::cerr << "An error occurred while trying to run a print operation:"
				<< ex.what() << std::endl;
	}
}

void Workspace::on_menu_file_print() {
	Glib::RefPtr<Gtk::PrintOperation> print = Gtk::PrintOperation::create();
	print_or_preview(print);

	try {
		print->run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG,
				dynamic_cast<Gtk::Window&> (*get_toplevel()));
	} catch (const Gtk::PrintError& ex) {
		std::cerr << "An error occurred while trying to run a print operation:"
				<< ex.what() << std::endl;
	}
}

void Workspace::on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& context,
		int page_nr) {
	if (page_nr == 0) {
		double sizeX, sizeY, sizePageX, sizePageY;
		getSize(sizeX, sizeY);
		getPageSize(sizePageX, sizePageY);
		double scaleX = sizePageX / sizeX;
		double scaleY = sizePageY / sizeY;
		double lowest = scaleX < scaleY ? scaleX : scaleY;
		if (lowest < 1)
			context->get_cairo_context()->scale(lowest, lowest);
		prepareScreenContext(context->get_cairo_context());
	} else{
	}
}


VisualDiagramComponent* Workspace::getVisualComponent(Component* comp){
	for ( unsigned i=0 ; i<m_visualComponentList.size(); i++){
		if ( m_visualComponentList[i]->getComponent() == comp)
			return m_visualComponentList[i];
	}
	return NULL;
}


void Workspace::updateDiagramSize(){

	int maxX = 0, maxY = 0, minX = 0, minY = 0;
	if(m_visualComponentList.size() > 0) {
		maxX = minX = m_visualComponentList[0]->getX();
		maxY = minY = m_visualComponentList[0]->getY();
		for ( unsigned i=1 ; i<m_visualComponentList.size(); i++){

			 if(m_visualComponentList[i]->getX() + m_visualComponentList[i]->getSizeX()> maxX)
				 maxX = m_visualComponentList[i]->getX() + m_visualComponentList[i]->getSizeX();
			 else if(m_visualComponentList[i]->getX() < minX)
				 minX = m_visualComponentList[i]->getX();

			 if(m_visualComponentList[i]->getY() + m_visualComponentList[i]->getSizeY() > maxY)
				 maxY = m_visualComponentList[i]->getY() + m_visualComponentList[i]->getSizeY();
			 else if(m_visualComponentList[i]->getY() < minY)
				 minY = m_visualComponentList[i]->getY();
		}
	 }

	m_diagramOffsetX = minX;
	m_diagramOffsetY = minY;
	m_diagram->setSize(maxX - minX, maxY - minY);
}

Diagram* Workspace::getDiagram(){
	return m_diagram;
}

void Workspace::getPageSize(double& sizeX, double& sizeY) {
    sizeY = m_refPageSetup->get_page_height(Gtk::UNIT_POINTS);
    sizeX = m_refPageSetup->get_page_width(Gtk::UNIT_POINTS);
}

VisualDiagramComponent* Workspace::getVisualComponent(unsigned id) {
    return m_visualComponentList[id];
}

void Workspace::getSize(double& sizeX, double& sizeY) {
    updateDiagramSize();
    sizeX = m_diagram->getSizeX() + m_diagramOffsetX;
    sizeY = m_diagram->getSizeY() + m_diagramOffsetY;
}

bool Workspace::alejarComponentesParaQueNoSePisen() {
	bool colision = false;
	unsigned int cantidadDeColisiones = 0;

	for (unsigned int iDelComponenteActual = 0; iDelComponenteActual < m_diagram->getComponents()->size(); iDelComponenteActual++) {
		Component *componenteActual = m_diagram->getComponents()->at(iDelComponenteActual);
		if (componenteActual->getComponentType() != ComponentTypeClasesClase) {
			continue;
		}
		VisualDiagramComponent *vistaDelComponenteActual = getVisualComponent(componenteActual);
		InfluenceArea areaDeinfluenciaDelComponenteActual = vistaDelComponenteActual->getInfluenceArea();
		for (unsigned int jDeOtroComponente = iDelComponenteActual + 1; jDeOtroComponente < m_diagram->getComponents()->size(); jDeOtroComponente++) {
			Component *otroComponente = m_diagram->getComponents()->at(jDeOtroComponente);
			if (otroComponente->getComponentType() != ComponentTypeClasesClase) {
				continue;
			}
			VisualDiagramComponent *vistaDelOtroComponente = getVisualComponent(otroComponente);
			InfluenceArea areaDeinfluenciaDelOtroComponente = vistaDelOtroComponente->getInfluenceArea();
			if (areaDeinfluenciaDelComponenteActual.touch(areaDeinfluenciaDelOtroComponente)) {
				cantidadDeColisiones ++;
				colision = true;
				vistaDelOtroComponente->setX(otroComponente->getX() + 1);
				otroComponente->setY(otroComponente->getY() + 1);
			}
		}
	}
	std::cout << "cantidad de colisiones: " << cantidadDeColisiones << std::endl;
	return colision;
}
